/**
 * Safe Logger Utility
 * Prevents EPIPE errors by safely handling console output
 */

import { writeFileSync, appendFileSync, existsSync } from 'fs'
import { join } from 'path'
import { app } from 'electron'

export enum LogLevel {
  DEBUG = 0,
  INFO = 1,
  WARN = 2,
  ERROR = 3
}

export interface LogEntry {
  timestamp: string
  level: LogLevel
  message: string
  data?: any
  source?: string
}

class Logger {
  private static instance: Logger
  private logLevel: LogLevel = LogLevel.INFO
  private logFile: string | null = null
  private consoleAvailable: boolean = true

  private constructor() {
    this.initializeLogFile()
    this.setupGracefulShutdown()
  }

  public static getInstance(): Logger {
    if (!Logger.instance) {
      Logger.instance = new Logger()
    }
    return Logger.instance
  }

  private initializeLogFile(): void {
    try {
      if (app && app.getPath) {
        const userDataPath = app.getPath('userData')
        this.logFile = join(userDataPath, 'intellimark.log')
      }
    } catch (error) {
      // Fallback to relative path if app path is not available
      this.logFile = './intellimark.log'
    }
  }

  private setupGracefulShutdown(): void {
    // Handle process exit to prevent EPIPE errors
    process.on('SIGPIPE', () => {
      this.consoleAvailable = false
    })

    process.on('exit', () => {
      this.consoleAvailable = false
    })

    // Handle stdout/stderr errors
    process.stdout.on('error', (error) => {
      if (error.code === 'EPIPE') {
        this.consoleAvailable = false
      }
    })

    process.stderr.on('error', (error) => {
      if (error.code === 'EPIPE') {
        this.consoleAvailable = false
      }
    })
  }

  private safeConsoleLog(level: 'log' | 'info' | 'warn' | 'error', ...args: any[]): void {
    if (!this.consoleAvailable) {
      return
    }

    try {
      // Use process.stdout.write directly for more control
      const message = args.map(arg =>
        typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
      ).join(' ')

      const timestamp = new Date().toISOString()
      const logMessage = `[${timestamp}] ${message}\n`

      if (level === 'error') {
        if (process.stderr.writable) {
          process.stderr.write(logMessage)
        }
      } else {
        if (process.stdout.writable) {
          process.stdout.write(logMessage)
        }
      }
    } catch (error) {
      // If console logging fails, disable it
      this.consoleAvailable = false
    }
  }

  private writeToFile(entry: LogEntry): void {
    if (!this.logFile) return

    try {
      const logLine = `${entry.timestamp} [${LogLevel[entry.level]}] ${entry.source ? `[${entry.source}] ` : ''}${entry.message}${entry.data ? ` ${JSON.stringify(entry.data)}` : ''}\n`
      appendFileSync(this.logFile, logLine, 'utf8')
    } catch (error) {
      // Silently fail if we can't write to file
    }
  }

  private createLogEntry(level: LogLevel, message: string, data?: any, source?: string): LogEntry {
    return {
      timestamp: new Date().toISOString(),
      level,
      message,
      data,
      source
    }
  }

  public debug(message: string, data?: any, source?: string): void {
    if (this.logLevel > LogLevel.DEBUG) return

    const entry = this.createLogEntry(LogLevel.DEBUG, message, data, source)
    this.writeToFile(entry)
    this.safeConsoleLog('log', `DEBUG: ${message}`, data)
  }

  public info(message: string, data?: any, source?: string): void {
    if (this.logLevel > LogLevel.INFO) return

    const entry = this.createLogEntry(LogLevel.INFO, message, data, source)
    this.writeToFile(entry)
    this.safeConsoleLog('log', `INFO: ${message}`, data)
  }

  public warn(message: string, data?: any, source?: string): void {
    if (this.logLevel > LogLevel.WARN) return

    const entry = this.createLogEntry(LogLevel.WARN, message, data, source)
    this.writeToFile(entry)
    this.safeConsoleLog('warn', `WARN: ${message}`, data)
  }

  public error(message: string, error?: Error | any, source?: string): void {
    const entry = this.createLogEntry(LogLevel.ERROR, message, error, source)
    this.writeToFile(entry)

    if (error instanceof Error) {
      this.safeConsoleLog('error', `ERROR: ${message}`, {
        name: error.name,
        message: error.message,
        stack: error.stack
      })
    } else {
      this.safeConsoleLog('error', `ERROR: ${message}`, error)
    }
  }

  public setLogLevel(level: LogLevel): void {
    this.logLevel = level
  }

  public getLogLevel(): LogLevel {
    return this.logLevel
  }

  public setLogFile(filePath: string): void {
    this.logFile = filePath
  }

  public getLogFile(): string | null {
    return this.logFile
  }

  // Convenience methods for different modules
  public ai(message: string, data?: any): void {
    this.info(message, data, 'AI')
  }

  public git(message: string, data?: any): void {
    this.info(message, data, 'Git')
  }

  public fileSystem(message: string, data?: any): void {
    this.info(message, data, 'FileSystem')
  }

  public security(message: string, data?: any): void {
    this.info(message, data, 'Security')
  }

  public version(message: string, data?: any): void {
    this.info(message, data, 'Version')
  }

  public conversation(message: string, data?: any): void {
    this.info(message, data, 'Conversation')
  }

  public analytics(message: string, data?: any): void {
    this.info(message, data, 'Analytics')
  }

  public placeholder(message: string, data?: any): void {
    this.info(message, data, 'Placeholder')
  }

  public shortcut(message: string, data?: any): void {
    this.info(message, data, 'Shortcut')
  }

  public ipc(message: string, data?: any): void {
    this.debug(message, data, 'IPC')
  }

  // Performance logging
  public time(label: string): void {
    this.debug(`Timer started: ${label}`, undefined, 'PERF')
    console.time(label)
  }

  public timeEnd(label: string): void {
    try {
      console.timeEnd(label)
      this.debug(`Timer ended: ${label}`, undefined, 'PERF')
    } catch (error) {
      // Ignore timer errors
    }
  }

  // Clear log file
  public clearLogFile(): void {
    if (!this.logFile) return

    try {
      writeFileSync(this.logFile, '', 'utf8')
      this.info('Log file cleared', undefined, 'Logger')
    } catch (error) {
      // Silently fail
    }
  }

  // Get recent log entries
  public getRecentLogEntries(count: number = 100): string[] {
    if (!this.logFile || !existsSync(this.logFile)) {
      return []
    }

    try {
      const content = require('fs').readFileSync(this.logFile, 'utf8')
      const lines = content.split('\n').filter(line => line.trim())
      return lines.slice(-count)
    } catch (error) {
      return []
    }
  }
}

// Export singleton instance and convenience functions
const logger = Logger.getInstance()

// Export convenience functions that match console API
export const log = {
  debug: (message: string, data?: any, source?: string) => logger.debug(message, data, source),
  info: (message: string, data?: any, source?: string) => logger.info(message, data, source),
  warn: (message: string, data?: any, source?: string) => logger.warn(message, data, source),
  error: (message: string, error?: Error | any, source?: string) => logger.error(message, error, source),
  ai: (message: string, data?: any) => logger.ai(message, data),
  git: (message: string, data?: any) => logger.git(message, data),
  fileSystem: (message: string, data?: any) => logger.fileSystem(message, data),
  security: (message: string, data?: any) => logger.security(message, data),
  version: (message: string, data?: any) => logger.version(message, data),
  conversation: (message: string, data?: any) => logger.conversation(message, data),
  analytics: (message: string, data?: any) => logger.analytics(message, data),
  placeholder: (message: string, data?: any) => logger.placeholder(message, data),
  shortcut: (message: string, data?: any) => logger.shortcut(message, data),
  ipc: (message: string, data?: any) => logger.ipc(message, data),
  time: (label: string) => logger.time(label),
  timeEnd: (label: string) => logger.timeEnd(label),
  setLogLevel: (level: LogLevel) => logger.setLogLevel(level),
  clearLog: () => logger.clearLogFile(),
  getRecentLogs: (count?: number) => logger.getRecentLogEntries(count)
}

export default logger